Unescape the replacement parameter of regex replace functions

Akinori MUSHA 9 years ago
parent
commit
ea115b6c61
2 changed files with 47 additions and 4 deletions
  1. 35 4
      app/concerns/liquid_interpolatable.rb
  2. 12 0
      spec/concerns/liquid_interpolatable_spec.rb

+ 35 - 4
app/concerns/liquid_interpolatable.rb

@@ -202,12 +202,12 @@ module LiquidInterpolatable
202 202
       end
203 203
     end
204 204
 
205
-    def regex_replace(input, regex, replacement = ''.freeze)
206
-      input.to_s.gsub(Regexp.new(regex), replacement.to_s)
205
+    def regex_replace(input, regex, replacement = nil)
206
+      input.to_s.gsub(Regexp.new(regex), unescape_replacement(replacement.to_s))
207 207
     end
208 208
 
209
-    def regex_replace_first(input, regex, replacement = ''.freeze)
210
-      input.to_s.sub(Regexp.new(regex), replacement.to_s)
209
+    def regex_replace_first(input, regex, replacement = nil)
210
+      input.to_s.sub(Regexp.new(regex), unescape_replacement(replacement.to_s))
211 211
     end
212 212
 
213 213
     private
@@ -221,6 +221,37 @@ module LiquidInterpolatable
221 221
           Logger.new(STDERR)
222 222
         end
223 223
     end
224
+
225
+    BACKSLASH = "\\".freeze
226
+
227
+    UNESCAPE = {
228
+      "a" => "\a",
229
+      "b" => "\b",
230
+      "e" => "\e",
231
+      "f" => "\f",
232
+      "n" => "\n",
233
+      "r" => "\r",
234
+      "s" => " ",
235
+      "t" => "\t",
236
+      "v" => "\v",
237
+    }
238
+
239
+    def unescape_replacement(s)
240
+      s.gsub(/\\(?:([\d+&`'\\]|k<\w+>)|u\{([[:xdigit:]]+)\}|x([[:xdigit:]]{2})|(.))/) {
241
+        if c = $1
242
+          BACKSLASH + c
243
+        elsif c = ($2 && [$2.to_i(16)].pack('U')) ||
244
+                  ($3 && [$3.to_i(16)].pack('C'))
245
+          if c == BACKSLASH
246
+            BACKSLASH + c
247
+          else
248
+            c
249
+          end
250
+        else
251
+          UNESCAPE[$4] || $4
252
+        end
253
+      }
254
+    end
224 255
   end
225 256
   Liquid::Template.register_filter(LiquidInterpolatable::Filters)
226 257
 

+ 12 - 0
spec/concerns/liquid_interpolatable_spec.rb

@@ -187,6 +187,12 @@ describe LiquidInterpolatable::Filters do
187 187
       agent.options['cleaned'] = '{{ something | regex_replace_first: "\S+bar", "foobaz"  }}'
188 188
       expect(agent.interpolated['cleaned']).to eq('foobaz foobar')
189 189
     end
190
+
191
+    it 'should support escaped characters' do
192
+      agent.interpolation_context['something'] = "foo\\1\n\nfoo\\bar\n\nfoo\\baz"
193
+      agent.options['test'] = "{{ something | regex_replace_first: '\\\\(\\w{2,})', '\\1\\\\' | regex_replace_first: '\\n+', '\\n'  }}"
194
+      expect(agent.interpolated['test']).to eq("foo\\1\nfoobar\\\n\nfoo\\baz")
195
+    end
190 196
   end
191 197
 
192 198
   describe 'regex_replace' do
@@ -197,5 +203,11 @@ describe LiquidInterpolatable::Filters do
197 203
       agent.options['cleaned'] = '{{ something | regex_replace: "\S+bar", "foobaz"  }}'
198 204
       expect(agent.interpolated['cleaned']).to eq('foobaz foobaz')
199 205
     end
206
+
207
+    it 'should support escaped characters' do
208
+      agent.interpolation_context['something'] = "foo\\1\n\nfoo\\bar\n\nfoo\\baz"
209
+      agent.options['test'] = "{{ something | regex_replace: '\\\\(\\w{2,})', '\\1\\\\' | regex_replace: '\\n+', '\\n'  }}"
210
+      expect(agent.interpolated['test']).to eq("foo\\1\nfoobar\\\nfoobaz\\")
211
+    end
200 212
   end
201 213
 end